<template>
  <div
    ref="triggerRef"
    class="dropdown-trigger"
    @click="handleClickTrigger"
    @mouseenter="handleHoverOpen"
    @mouseleave="handleHoverClose"
  >
    <slot></slot>
  </div>

  <teleport to="body" :disabled="!props.teleported">
    <div
      v-if="props.persistent ? true : visible"
      v-show="visible"
      ref="floatingRef"
      :style="combinedFloatingStyles"
      class="popup-menu"
      :class="props.customClass"
      role="menu"
      @mouseenter="handleHoverOpen"
      @mouseleave="handleHoverClose"
    >
      <slot name="menu"></slot>

      <div
        v-if="props.showArrow"
        ref="arrowRef"
        class="dropdown-arrow"
        :style="arrowStyle"
      ></div>
    </div>
  </teleport>
</template>

<script setup lang="ts">
import {
  ref,
  computed,
  watch,
  provide,
  type CSSProperties,
  onUnmounted,
} from "vue";
import {
  useFloating,
  offset,
  flip,
  shift,
  arrow,
  autoUpdate,
} from "@floating-ui/vue";
import {
  DropdownContextKey,
  type Placement,
  type DropdownMenuProps,
} from "./dropdown.types";
import {
  closeAllToolbarPopups,
  onCloseAllToolbarPopups,
  CLOSE_ALL_TOOLBAR_POPUPS_EVENT,
  type CloseAllToolbarPopupsEventDetail,
} from "../../button/src/utils/toolbarPopupManager";
import "./Dropdown.scss";

defineOptions({
  name: "DropdownMenu",
});

const props = withDefaults(defineProps<DropdownMenuProps>(), {
  placement: "bottom",
  offset: 8,
  trigger: "click",
  disabled: false,
  hideOnClick: true,
  teleported: true,
  persistent: false,
  showArrow: false,
  customStyle: () => ({}),
});

const emit = defineEmits<{
  (e: "visible-change", visible: boolean): void;
  (e: "command", command: any): void;
}>();

const visible = ref(false);
let hoverTimer: NodeJS.Timeout | null = null;

const triggerRef = ref<HTMLElement | null>(null); // 触发器
const floatingRef = ref<HTMLElement | null>(null); // 浮动菜单
const arrowRef = ref<HTMLElement | null>(null); // 箭头

const placementRef = computed(() => props.placement);

const middleware = computed(() => {
  const stack = [
    offset(props.offset),
    flip(), // 超出视口时翻转
    shift({ padding: 5 }), // 超出视口时移动
  ];
  if (props.showArrow) {
    stack.push(arrow({ element: arrowRef }));
  }
  return stack;
});

// useFloating (移除所有交互)
const { floatingStyles, middlewareData } = useFloating(
  triggerRef, // 依附的元素
  floatingRef, // 浮动的元素
  {
    placement: placementRef,
    middleware: middleware,
    whileElementsMounted: autoUpdate, // 自动处理 resize, scroll
  }
);

const open = () => {
  if (props.disabled) return;
  // 打开时，先关闭所有其他弹窗
  closeAllToolbarPopups(triggerRef.value, "dropdown");
  visible.value = true;
};
const close = () => {
  visible.value = false;
};
const toggle = () => {
  visible.value ? close() : open();
};

// Click Trigger
const handleClickTrigger = () => {
  if (props.trigger === "click") {
    toggle();
  }
};

// Hover Trigger
const handleHoverOpen = () => {
  if (props.trigger !== "hover") return;
  if (hoverTimer) {
    clearTimeout(hoverTimer);
    hoverTimer = null;
  }
  open();
};

const handleHoverClose = () => {
  if (props.trigger !== "hover") return;
  if (hoverTimer) clearTimeout(hoverTimer);

  hoverTimer = setTimeout(() => {
    close();
  }, 100); // 100ms 延迟关闭
};

// Click Outside
const handleClickOutside = (event: MouseEvent) => {
  if (!visible.value) return;

  const target = event.target as Node;

  const isClickOnTrigger = triggerRef.value?.contains(target);
  const isClickOnFloating = floatingRef.value?.contains(target);
  
  // 检查点击是否在按钮组容器内（主按钮区域）
  // 如果 triggerRef 的父元素是 toolbar-dropdown-button-group，也要检查这个容器
  const buttonGroup = triggerRef.value?.parentElement?.parentElement;
  const isClickOnButtonGroup = buttonGroup?.classList.contains('toolbar-dropdown-button-group') 
    ? buttonGroup.contains(target) 
    : false;

  // 检查是否点击在主按钮上（主按钮在 triggerRef 的兄弟元素中）
  const iconsWrapper = triggerRef.value?.parentElement;
  const mainButton = iconsWrapper?.querySelector('.toolbar-dropdown-main-button');
  const isClickOnMainButton = mainButton?.contains(target) || false;

  // 如果 trigger 是 'click' 模式, 点击 trigger 或主按钮应该切换, 这里已由 handleClickTrigger 和 handleMainClick 处理
  // 我们只需要处理点击 *外部*
  if (props.trigger === "click" && (isClickOnTrigger || isClickOnButtonGroup || isClickOnMainButton)) {
    return;
  }

  if (!isClickOnTrigger && !isClickOnFloating && !isClickOnButtonGroup && !isClickOnMainButton) {
    close();
  }
};

// 监听 visible 状态来添加/移除全局点击监听器
watch(
  visible,
  (newVisible) => {
    if (newVisible) {
      document.addEventListener("click", handleClickOutside, true);
    } else {
      document.removeEventListener("click", handleClickOutside, true);
    }
    emit("visible-change", newVisible);
  },
  { flush: "post" }
);

// 监听关闭所有弹窗的事件
const handleCloseAllPopups = (
  event: CustomEvent<CloseAllToolbarPopupsEventDetail>
) => {
  const { currentInstance, excludeTypes } = event.detail;
  // 如果当前实例不是当前 Dropdown，或者被排除，则关闭
  if (
    currentInstance !== triggerRef.value &&
    !excludeTypes?.includes("dropdown") &&
    visible.value
  ) {
    close();
  }
};

let cleanupPopupListener: (() => void) | null = null;

// 在组件挂载时添加弹窗事件监听
watch(
  () => visible.value,
  (newVisible) => {
    if (newVisible && !cleanupPopupListener) {
      cleanupPopupListener = onCloseAllToolbarPopups(handleCloseAllPopups);
    }
  },
  { immediate: true }
);

// 确保组件卸载时移除监听器
onUnmounted(() => {
  document.removeEventListener("click", handleClickOutside, true);
  if (cleanupPopupListener) {
    cleanupPopupListener();
    cleanupPopupListener = null;
  }
});

provide(DropdownContextKey, {
  handleItemClick: (command: any) => {
    // 发出 command 事件
    if (command !== undefined) {
      emit("command", command);
    }
    // 如果 hideOnClick, 则关闭菜单
    if (props.hideOnClick) {
      close();
    }
  },
});

// 提供 maxHeight 给子菜单使用
provide(
  "dropdownMaxHeight",
  computed(() => props.maxHeight)
);

const combinedFloatingStyles = computed<CSSProperties>(() => {
  const styles: CSSProperties = {
    ...props.customStyle,
    ...floatingStyles.value,
  };
  // 只有在用户设置了 maxHeight 时才应用
  if (props.maxHeight) {
    const height =
      typeof props.maxHeight === "number"
        ? `${props.maxHeight}px`
        : props.maxHeight;
    styles.maxHeight = height;
    styles.overflowY = "auto"; // 有 maxHeight 时才启用滚动
    styles.overflowX = "visible"; // 允许横向溢出以支持子菜单
  } else {
    // 没有 maxHeight 时，不限制高度，也不显示滚动条
    // 允许溢出以支持子菜单显示
    styles.overflowY = "visible";
    styles.overflowX = "visible";
  }
  return styles;
});

const arrowStyle = computed<CSSProperties>(() => {
  if (!props.showArrow || !middlewareData.value?.arrow) return {};
  const { x, y } = middlewareData.value.arrow;

  // 确定箭头在哪条边
  const side = middlewareData.value.placement.split("-")[0];

  const staticSide: { [key: string]: string } = {
    top: "bottom",
    right: "left",
    bottom: "top",
    left: "right",
  };

  return {
    left: x != null ? `${x}px` : "",
    top: y != null ? `${y}px` : "",
    [staticSide[side]]: "-5px", // 箭头尖端往外移
    background: "white",
  };
});

// 暴露方法给父组件
defineExpose({
  toggle,
  open,
  close,
});
</script>
